#include "transfile_server.h"

#include "struct_tlv.h"

#include "log.h"

int gets_server(userOpInfo_t *userOpInfo){
    tlvPacket_t packet;
    memset(&packet, 0, sizeof(packet));

    // 接受文件名
    recv(userOpInfo->netfd, (char*)&packet.type, sizeof(packet.type), MSG_WAITALL);
    char filename[1024] = {0};
    recv(userOpInfo->netfd, (char *)&packet.length, sizeof(packet.length), MSG_WAITALL);
    recv(userOpInfo->netfd, packet.value, packet.length, MSG_WAITALL);
    memcpy(filename, packet.value, packet.length);
    
    // 判断文件名对应的以hash值为文件名的文件是否存在
    char sql[4096] = {0};
    sprintf(sql, "select hash from file_info where filename = '%s' and username = '%s' and parent_id = %d;",
            filename, userOpInfo->userName, userOpInfo->ID);

    mysql_query(userOpInfo->mysql, sql);
    MYSQL_RES *result = mysql_store_result(userOpInfo->mysql);
    MYSQL_ROW row = mysql_fetch_row(result);
    char hash[41] = {0};
    memcpy(hash, row[0], strlen(row[0]));

    // 拼接真实路径
    char workPath[4096] = {0};
    strcat(workPath, userOpInfo->addr);
    strcat(workPath, "/");
    strcat(workPath, hash);

    //对文件是否存在进行错误处理
    int error_flag = 1;
    if(access(workPath,F_OK) != 0){
        error_flag = 0;
        send(userOpInfo->netfd, &error_flag, sizeof(error_flag), MSG_NOSIGNAL);
        printf("\n");
        return -1;
    }
    send(userOpInfo->netfd, &error_flag, sizeof(error_flag), MSG_NOSIGNAL);

    //打开需下载文件的fd
    int fd = open(workPath, O_RDONLY);
    ERROR_CHECK(fd, -1, "open");

    // 获取发送文件大小
    struct stat statbuf;
    fstat(fd, &statbuf);
    off_t filesize = statbuf.st_size;

    // 发送文件大小
    memset(&packet, 0, sizeof(packet));
    packet.type = GETS;
    packet.length = sizeof(filesize);
    memcpy(packet.value, &filesize, packet.length);
    send(userOpInfo->netfd, (char *)&packet, sizeof(packet.type) + sizeof(packet.length) + packet.length, MSG_NOSIGNAL);

    // 若传输文件大于100M, 采用mmap
    if(filesize > 104857600){
        ftruncate(fd, filesize);
        char *p = (char *)mmap(NULL, filesize, PROT_READ, MAP_SHARED, fd, 0);
        send(userOpInfo->netfd, p, filesize, MSG_NOSIGNAL);
        munmap(p, filesize);
    }else{
        while(1){
            memset(&packet, 0, sizeof(packet));
            packet.type = GETS;
            packet.length = read(fd, packet.value, sizeof(packet.value));
            if(packet.length > 0){
                send(userOpInfo->netfd, (char *)&packet, sizeof(packet.type) + sizeof(packet.length) + packet.length, MSG_NOSIGNAL);
            }else{
                break;
            }
        }

        // 发送结束标志
        memset(&packet, 0, sizeof(packet));
        packet.type = GETS;
        send(userOpInfo->netfd, (char *)&packet, sizeof(packet.type) + sizeof(packet.length) + packet.length, MSG_NOSIGNAL);
    }

    close(fd);

    printf("send complete\n");
    return 1;
}

int puts_server(userOpInfo_t *userOpInfo) {
    tlvPacket_t packet;
    int ifExist = 1; // 文件是否存在
    int isQuickUpload = 0; // 新增标志：是否是秒传

    // 接收文件是否存在标志
    recv(userOpInfo->netfd, &ifExist, sizeof(ifExist), MSG_WAITALL);
    if (ifExist == -1) {
        printf("filename error, please input again!\n");
        return -1;
    }

    // 接收文件hash
    char hash[41] = {0};
    memset(&packet, 0, sizeof(packet));
    recv(userOpInfo->netfd, &packet.type, sizeof(packet.type), MSG_WAITALL);
    recv(userOpInfo->netfd, &packet.length, sizeof(packet.length), MSG_WAITALL);
    recv(userOpInfo->netfd, hash, packet.length, MSG_WAITALL);
    printf("hash = %s\n", hash);

    memset(&packet, 0, sizeof(packet));
    // 接收文件名
    char filename[1024] = {0};
    recv(userOpInfo->netfd, &packet.type, sizeof(packet.type), MSG_WAITALL);
    recv(userOpInfo->netfd, &packet.length, sizeof(packet.length), MSG_WAITALL);
    recv(userOpInfo->netfd, packet.value, packet.length, MSG_WAITALL);
    memcpy(filename, packet.value, packet.length);

    // 拼接真实存储路径（基于hash）
    char workPath[1024] = {0};
    strcat(workPath, userOpInfo->addr);
    strcat(workPath, "/");
    strcat(workPath, hash);

    // 检查是否已存在完全相同的文件（相同hash、相同username、相同parentID、相同filename）
    char sql[4096] = {0};
    sprintf(sql, "SELECT * FROM file_info WHERE hash = '%s' AND username = '%s' AND parent_id = %d AND filename = '%s';",
            hash, userOpInfo->userName, userOpInfo->ID, filename);
    mysql_query(userOpInfo->mysql, sql);
    MYSQL_RES *result = mysql_store_result(userOpInfo->mysql);
    MYSQL_ROW row = mysql_fetch_row(result);

    if (row != NULL) {
        // 文件已存在（完全相同的记录），直接返回成功
        int transFlag = 2;
        send(userOpInfo->netfd, &transFlag, sizeof(transFlag), MSG_NOSIGNAL);
        printf("File already exists, no changes made.\n");
        mysql_free_result(result);
        return 1;
    }

    // 检查文件内容是否已存在（仅hash相同）
    int existingQuotenum = 1;
    memset(sql, 0, sizeof(sql));
    sprintf(sql, "SELECT quotenum FROM file_info WHERE hash = '%s' LIMIT 1;", hash);
    mysql_query(userOpInfo->mysql, sql);
    result = mysql_store_result(userOpInfo->mysql);
    row = mysql_fetch_row(result);

    if (row != NULL) {
        // 文件内容已存在（hash相同但路径不同），允许秒传
        isQuickUpload = 1; // 设置秒传标志
        existingQuotenum = atoi(row[0]);
        int transFlag = 1;
        send(userOpInfo->netfd, &transFlag, sizeof(transFlag), MSG_NOSIGNAL);
    } else {
        // 文件内容不存在，需要完整传输
        int transFlag = 0;
        send(userOpInfo->netfd, &transFlag, sizeof(transFlag), MSG_NOSIGNAL);

        // 打开文件并接收内容
        int fd = open(workPath, O_RDWR | O_CREAT | O_TRUNC, 0666);
        printf("workPath = %s\n", workPath);
        ERROR_CHECK(fd, -1, "open");

        // 接收文件大小
        off_t filesize;
        memset(&packet, 0, sizeof(packet));
        recv(userOpInfo->netfd, (char *)&packet.type, sizeof(packet.type), MSG_WAITALL);
        recv(userOpInfo->netfd, (char *)&packet.length, sizeof(packet.length), MSG_WAITALL);
        recv(userOpInfo->netfd, packet.value, packet.length, MSG_WAITALL);
        filesize = *((int *)packet.value);
        memcpy(&filesize, packet.value, packet.length);

        // 传输文件内容（mmap或分块传输）
        if (filesize > 104857600) {
            ftruncate(fd, filesize);
            char *p = (char *)mmap(NULL, filesize, PROT_WRITE, MAP_SHARED, fd, 0);
            recv(userOpInfo->netfd, p, filesize, MSG_WAITALL);
            munmap(p, filesize);
        } else {
            while (1) {
                memset(&packet, 0, sizeof(packet));
                recv(userOpInfo->netfd, (char *)&packet.type, sizeof(packet.type), MSG_WAITALL);
                recv(userOpInfo->netfd, (char *)&packet.length, sizeof(packet.length), MSG_WAITALL);
                if (packet.length > 0) {
                    recv(userOpInfo->netfd, packet.value, packet.length, MSG_WAITALL);
                    write(fd, packet.value, packet.length);
                } else {
                    break;
                }
            }
        }
        close(fd);
    }

    // 插入新记录
    char *userName = userOpInfo->userName;
    int parentID = userOpInfo->ID;
    char type = 'f';
    char bufSql[4096] = {0};

    // 拼接path
    sprintf(bufSql, "SELECT path FROM file_info WHERE id = %d;", userOpInfo->ID);
    mysql_query(userOpInfo->mysql, bufSql);
    result = mysql_store_result(userOpInfo->mysql);
    row = mysql_fetch_row(result);
    char curPath[1024] = {0};
    if (row != NULL) {
        memcpy(curPath, row[0], strlen(row[0]));
        strcat(curPath, filename);
    }

    // 插入数据库（quotenum=existingQuotenum）
    memset(bufSql, 0, sizeof(bufSql));
    sprintf(bufSql, "INSERT INTO file_info (filename, username, parent_id, hash, type, path, quotenum) "
            "VALUES ('%s', '%s', %d, '%s', '%c', '%s', %d);",
            filename, userName, parentID, hash, type, curPath, existingQuotenum);
    mysql_query(userOpInfo->mysql, bufSql);

    // 如果是秒传，更新所有相同hash记录的quotenum（包括刚插入的）
    if (isQuickUpload) {
        memset(bufSql, 0, sizeof(bufSql));
        sprintf(bufSql, "UPDATE file_info SET quotenum = quotenum + 1 WHERE hash = '%s';", hash);
        mysql_query(userOpInfo->mysql, bufSql);
    }

    printf("File upload complete.\n");
    if (result) mysql_free_result(result);
    return 1;
}
